#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wifi_default.h"
#include "esp_bit_defs.h"
#include "esp_netif.h"
#include "esp_netif_ip_addr.h"
#include "esp_event.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "esp_sntp.h"
#include "esp_netif.h"
#include "nvs.h"
#include "nvs_flash.h"

#include "wifistation.h"
#include "storage.h"
#include "softAP.h"
#include "http_server.h"

#define NOTIFY

static const char *TAG = "wifi-station";

static wifi_config_t wifi_sta_config;

static uint8_t wifi_status = WIFI_EVENT_STA_DISCONNECTED;
#ifdef NOTIFY
EventGroupHandle_t s_wifi_event_group;
#endif
char st_ssid[32] = "";
char st_password[32] = "";
uint8_t local_ip[4];

extern uint8_t need_reboot;
extern uint8_t identifyBuf[32];

static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
	static uint8_t retryCount = 0;
	int int_ip = 0; 

	if (WIFI_EVENT == event_base && WIFI_EVENT_STA_START == event_id) {
		esp_wifi_connect();
		wifi_status = WIFI_EVENT_STA_START;
	} else if (WIFI_EVENT == event_base && WIFI_EVENT_STA_DISCONNECTED == event_id) {
		wifi_event_sta_disconnected_t *disconnect = (wifi_event_sta_disconnected_t *)event_data;
		ESP_LOGE(TAG, "Wifi Disconnect reason: %d", disconnect->reason);
		if(retryCount >= 3)
		{
			writeConfigToNvs("ssid", strlen("ssid"), st_ssid, 0);
    		writeConfigToNvs("password", strlen("password"), st_password, 0);
			need_reboot = 1;
		}
		else
		{
			retryCount++;
			esp_wifi_connect();
		}
		wifi_status = WIFI_EVENT_STA_DISCONNECTED;
		memset(local_ip, 0, 4);
		#ifdef NOTIFY
		xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
		#endif
	} else if (IP_EVENT == event_base && IP_EVENT_STA_GOT_IP == event_id) {
		wifi_status = IP_EVENT_STA_GOT_IP;
		retryCount = 0;
		ip_event_got_ip_t *ip = (ip_event_got_ip_t *)event_data;
		// ESP_LOGI(TAG, "Got Ip Address " "%d.%d.%d.%d", IP2STR(&ip->ip_info.ip));
		int_ip = ip->ip_info.ip.addr;
		local_ip[0] = *((unsigned char *)&int_ip);
		local_ip[1] = *((unsigned char *)&int_ip+1);
		local_ip[2] = *((unsigned char *)&int_ip+2);
		local_ip[3] = *((unsigned char *)&int_ip+3);

		ESP_LOGI(TAG, "Got Ip Address %d.%d.%d.%d\r\n", local_ip[0],local_ip[1],local_ip[2],local_ip[3]);
		
		memset(st_ssid, 0, sizeof(st_ssid));
		memcpy(st_ssid, wifi_sta_config.sta.ssid, strlen((const char *)wifi_sta_config.sta.ssid));

		memset(st_password, 0, sizeof(st_password));
		memcpy(st_password, wifi_sta_config.sta.password, strlen((const char *)wifi_sta_config.sta.password));

		if((strlen(st_ssid) != 0) && (strlen(st_password) != 0))
		{
			writeConfigToNvs("ssid", strlen("ssid"), st_ssid, strlen(st_ssid));
    		writeConfigToNvs("password", strlen("password"), st_password, strlen(st_password));
		}
		#ifdef NOTIFY
		xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
		#endif
	}
	else if (event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
                 MAC2STR(event->mac), event->aid);
    } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
	else
	{
		wifi_status = (int)event_id;
		ESP_LOGI(TAG, "--------Wifi Event %d", (int)event_id);
	}
}

uint8_t *get_ssid_of_station(void)
{
	return (wifi_sta_config.sta.ssid);
}

uint8_t get_wifi_status(void)
{
	return wifi_status;
}

int8_t get_rssi_of_station(void)
{
    uint16_t number = 1;
    uint16_t ap_count = 0;

    wifi_ap_record_t ap_info[1];
    wifi_config_t wifi_sta_cfg;

    ESP_LOGI(TAG,"start scan");
    memset(ap_info, 0, sizeof(ap_info));
    if (esp_wifi_get_config(WIFI_IF_STA, &wifi_sta_cfg) != ESP_OK)//获取已连接的ap参数
    {
        ESP_LOGI(TAG, "esp_wifi_get_config err");  
        return 1;
    }

    wifi_scan_config_t scan_config = { 0 };
    scan_config.ssid = wifi_sta_cfg.sta.ssid;//限制扫描的ap的ssid
    scan_config.bssid = wifi_sta_cfg.sta.bssid;//限制扫描的ap的mac地址
    esp_wifi_scan_start(&scan_config, true);//阻塞扫描ap，scan_config为扫描的参数
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));//获取扫描到的ap信息
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
    //获取扫描到的ap数量，因为限制了ssid和mac，因此最多只会扫描到1个
    for (int i = 0; (i < 1) && (i < ap_count); i++) {
        ESP_LOGI(TAG, "SSID:%s; RSSI:%d; Channel:%d; BSSID:%02X:%02X:%02X:%02X:%02X:%02X", 
		         ap_info[i].ssid, ap_info[i].rssi, ap_info[i].primary, 
				 ap_info[i].bssid[0],ap_info[i].bssid[1],ap_info[i].bssid[2],
				 ap_info[i].bssid[3],ap_info[i].bssid[4],ap_info[i].bssid[5]);
    }
    esp_wifi_scan_stop(); 
    //from start to stop need 3210ms
    ESP_LOGI(TAG,"stop scan\r\n");
	return 0;
}

void getCurrentDateTime(char* buf)
{
	struct tm timeinfo;
	time_t now;
	char time_buf[32];

	time(&now);                         // 获取网络时间, 64bit的秒计数 
	tzset();                            // 更新本地C库时间
	localtime_r(&now, &timeinfo);       // 转换成具体的时间参数
	strftime((char*)time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
	memcpy(buf, time_buf, sizeof(time_buf));
}

void getCurrentTime(char* buf)
{
	struct tm timeinfo;
	time_t now;
	char time_buf[32];

	time(&now);                         // 获取网络时间, 64bit的秒计数 
	tzset();                            // 更新本地C库时间
	localtime_r(&now, &timeinfo);       // 转换成具体的时间参数
	strftime((char*)time_buf, sizeof(time_buf), "%H:%M:%S", &timeinfo);
	memcpy(buf, time_buf, sizeof(time_buf));
}

void initialize_sntp(void)
{
	struct tm timeinfo;
	time_t now;
	char strftime_buf[32];

	esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);// 设置单播模式
	esp_sntp_setservername(0, "cn.ntp.org.cn");
	esp_sntp_setservername(1, "ntp1.aliyun.com");
	esp_sntp_setservername(2, "210.72.145.44");
    setenv("TZ", "CST-8", 1);                   //东八区
    esp_sntp_init();                                //初始化

	do {
        vTaskDelay(100 / portTICK_PERIOD_MS);
    } while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET);
	esp_sntp_stop();

	time(&now);                         // 获取网络时间, 64bit的秒计数 
	tzset();                            // 更新本地C库时间
	localtime_r(&now, &timeinfo);       // 转换成具体的时间参数
	// strftime((char*)strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
	// ESP_LOGI(TAG, "The current time in Shanghai is: %s", strftime_buf);
	// ESP_LOGI(TAG, "%4d-%02d-%02d %02d:%02d:%02d week:%d", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, timeinfo.tm_wday); 
	getCurrentDateTime(strftime_buf);
	ESP_LOGI(TAG, "In Shanghai is:%s", strftime_buf);
}

void resetWifi(void)
{
	writeConfigToNvs("ssid", 4, "", 0);
	writeConfigToNvs("password", 8, "", 0);
}

void wifiStationInit(char* sta_ssid, char* sta_pwd)
{
    ESP_LOGI(TAG, "%s %d", __func__, __LINE__);
	#ifdef NOTIFY
	s_wifi_event_group = xEventGroupCreate();
	#endif
	esp_netif_create_default_wifi_sta();

	esp_event_handler_instance_t any_id;
	esp_event_handler_instance_t got_ip;
	ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &any_id));
	ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &got_ip));

	ESP_LOGI(TAG, "initialize connect wifi %s %d %s %d", 
		sta_ssid, strlen(sta_ssid), sta_pwd, strlen(sta_pwd));

	wifi_sta_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK,//ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD
	// wifi_sta_config.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
	// wifi_sta_config.sta.pmf_cfg.capable = true;
	// wifi_sta_config.sta.pmf_cfg.required = false;
	memset(wifi_sta_config.sta.ssid, 0, sizeof(wifi_sta_config.sta.ssid));
	memcpy(wifi_sta_config.sta.ssid, sta_ssid, strlen((const char *)sta_ssid));

	memset(wifi_sta_config.sta.password, 0, sizeof(wifi_sta_config.sta.password));
	memcpy(wifi_sta_config.sta.password, sta_pwd, strlen((const char *)sta_pwd));

	// esp_wifi_set_storage(WIFI_STORAGE_RAM);
	ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config));

	ESP_ERROR_CHECK(esp_wifi_start());
	#ifdef NOTIFY
	EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
		WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
		pdFALSE,
		pdFALSE,
		portMAX_DELAY);

	if (bits & WIFI_CONNECTED_BIT) {
		// ESP_LOGI(TAG, "already connected to ap SSID:%s password:%s",
		// 		sta_ssid, sta_pwd);
				initialize_sntp();

				readConfigFromNvs("identify", strlen("identify"), (char*)identifyBuf, 32);
	} else if (bits & WIFI_FAIL_BIT) {
		ESP_LOGI(TAG, "Failed to connect to SSID:%s", sta_ssid);
	} else {
		ESP_LOGE(TAG, "UNEXPECTED EVENT");
	}
	#endif
	APWebServerStart();	
}
